"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EcsWorld = void 0;
var EcsEntity_1 = require("./EcsEntity");
var EcsEntityGroup_1 = require("./EcsEntityGroup");
var EcsComponent_1 = require("./EcsComponent");
var Utils_1 = require("./Utils");
var EcsWorld = /** @class */ (function () {
    function EcsWorld(frameRate) {
        if (frameRate === void 0) { frameRate = 59; }
        this.nextEntityGenId = 10000;
        /**剩余待计入流逝的时间 */
        this.leftElapsed = 0;
        this._elapsed_per_frame = 20;
        this._exit = false;
        this.entities = {};
        this.singletons = [];
        this.singletonsMap = new Map();
        /**实体map池 */
        this._entityPoolMap = {};
        /**待回收实体 */
        this._recover_entities = [];
        this.systems = [];
        this._systemsMap = new Map();
        this.groupMap = new Map();
        this._enable = false;
        /**累计完成帧数 */
        this.frameCount = 0;
        this.lockFrame = -1;
        this.startTimeMs = 0;
        this._elapsed_per_frame = Math.round(1000 / frameRate);
        this.initSingletons();
        this.initEntityTypes();
        this.initSystems();
    }
    Object.defineProperty(EcsWorld.prototype, "enable", {
        get: function () {
            return this._enable;
        },
        set: function (value) {
            this._enable = value;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(EcsWorld.prototype, "frameRate", {
        get: function () { return Math.round(1000 / this._elapsed_per_frame); },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(EcsWorld.prototype, "elapsedMsPerFrame", {
        /**每帧流逝时间（毫秒） */
        get: function () { return this._elapsed_per_frame; },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(EcsWorld.prototype, "curTimeMs", {
        /**
         * 游戏世界以开始时间为现实时间算的当前时间，如果游戏运行速度不是1，这个时间会比现实时间快或慢
         */
        get: function () {
            return this.startTimeMs + this.frameCount * this.elapsedMsPerFrame;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(EcsWorld.prototype, "elapsedTimeMs", {
        /**
         * 游戏世界已流逝的时间(毫秒)
         */
        get: function () {
            return this.frameCount * this.elapsedMsPerFrame;
        },
        enumerable: false,
        configurable: true
    });
    EcsWorld.prototype.destory = function () {
        for (var k in this.entities) {
            this.removeEntity(this.entities[k].id);
        }
        for (var i = 0; i < this.systems.length; i++) {
            this.systems[i].destroy(this);
        }
    };
    EcsWorld.prototype.exit = function () {
        this._exit = true;
    };
    EcsWorld.prototype.isExit = function () {
        return this._exit;
    };
    /**注册实体类型对象池 */
    EcsWorld.prototype.regEntityPool = function (components) {
        var args = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            args[_i - 1] = arguments[_i];
        }
        var compIds = components.map(function (comp) { return EcsComponent_1.EcsComponent.getIdFromClz(comp); });
        var groupId = EcsEntity_1.EcsEntity.getGroupID(compIds);
        if (this._entityPoolMap[groupId]) {
            throw Error("register duplicate entity pool:" + groupId);
        }
        var _type = new (EcsEntity_1.EntityPool.bind.apply(EcsEntity_1.EntityPool, __spreadArray([void 0, components], args, false)))();
        this._entityPoolMap[groupId] = _type;
        return _type;
    };
    /**添加实体 */
    EcsWorld.prototype.addEntityByGroupId = function (groupId, cb, id) {
        if (id === void 0) { id = 0; }
        if (!id) {
            id = this.nextEntityGenId++;
        }
        if (!this._entityPoolMap[groupId]) {
            throw Error("not register entity pool:" + groupId);
        }
        return this.addEntityWithID(id, groupId, cb);
    };
    /** */
    EcsWorld.prototype.addEntityWithComps = function (compClzs, cb, id) {
        if (id === void 0) { id = 0; }
        var compIds = compClzs.map(function (comp) { return EcsComponent_1.EcsComponent.getIdFromClz(comp); });
        var groupId = EcsEntity_1.EcsEntity.getGroupID(compIds);
        if (!this._entityPoolMap[groupId]) {
            this.regEntityPool(compClzs, groupId);
        }
        return this.addEntityByGroupId(groupId, cb, id);
    };
    EcsWorld.prototype.getEntityPool = function (groupId, compClzs) {
        var pool = this._entityPoolMap[groupId];
        if (!pool) {
            pool = this.regEntityPool(compClzs, groupId);
        }
        return this._entityPoolMap[groupId];
    };
    /**指定id添加实体 */
    EcsWorld.prototype.addEntityWithID = function (id, groupId, cb) {
        var _type = this._entityPoolMap[groupId];
        var _entity = _type.create(id);
        this.entities[_entity.id] = _entity;
        this.groupMap.forEach(function (group) {
            if (group.isMatch(_entity)) {
                group.add(_entity);
            }
        });
        cb && cb(_entity);
        if (this.enable) {
            this.afterEntityCreated(_entity);
        }
        return _entity;
    };
    /**添加单例 */
    EcsWorld.prototype.addSingleton = function (singleton) {
        this.singletonsMap.set(singleton.constructor, singleton);
        this.singletons.push(singleton);
        return singleton;
    };
    /**获取单例 */
    EcsWorld.prototype.getSingleton = function (singletonClz) {
        return this.singletonsMap.get(singletonClz);
    };
    /**移除实体 */
    EcsWorld.prototype.removeEntity = function (id) {
        var _entity = this.entities[id];
        if (!_entity) {
            return;
        }
        if (this.enable) {
            this.beforeEntityRemove(_entity);
        }
        this.groupMap.forEach(function (group) {
            if (group.hasEntity(_entity)) {
                group.remove(_entity);
            }
        });
        if (_entity.destroy()) {
            this._recover_entities.push(_entity);
        }
        //this.entities[id] = null
        delete (this.entities[id]);
    };
    EcsWorld.prototype.getEntity = function (id) {
        return this.entities[id];
    };
    EcsWorld.prototype.getGroupByClz = function () {
        var clzs = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            clzs[_i] = arguments[_i];
        }
        return this.getGroupByCompIds.apply(this, clzs.map(function (clz) { return EcsComponent_1.EcsComponent.getIdFromClz(clz); }));
    };
    EcsWorld.prototype.getGroupByCompIds = function () {
        var compIds = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            compIds[_i] = arguments[_i];
        }
        var groupId = EcsEntity_1.EcsEntity.getGroupID(compIds);
        return this.getGroupByGroupId.apply(this, __spreadArray([groupId], compIds, false));
    };
    EcsWorld.prototype.getGroupByGroupId = function (groupId) {
        var compIds = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            compIds[_i - 1] = arguments[_i];
        }
        var group = this.groupMap.get(groupId);
        if (!group) {
            group = new EcsEntityGroup_1.EcsEntityGroup(compIds, this.entities);
            this.groupMap.set(groupId, group);
        }
        return group;
    };
    EcsWorld.prototype.addSystem = function (system) {
        if (system) {
            this._systemsMap.set(system.constructor, system);
            this.systems.push(system);
        }
    };
    EcsWorld.prototype.system = function (systemClz) {
        return this._systemsMap.get(systemClz);
    };
    EcsWorld.prototype.synFrame = function (frame) {
        var _delta_frame = frame - this.frameCount;
        var _delta_elapsed = _delta_frame * 1000 / this.frameRate;
        if (_delta_frame < 0) {
            this.leftElapsed = _delta_elapsed;
        }
        else {
            this.update(_delta_elapsed);
        }
    };
    EcsWorld.prototype.update = function (elapsedMs) {
        if (!this.enable) {
            return;
        }
        if (this.frameCount == 0) {
            this.startTimeMs = Date.now();
        }
        ;
        elapsedMs = (0, Utils_1.fixNumber)(elapsedMs + this.leftElapsed);
        while ((0, Utils_1.fixNumber)(elapsedMs - this._elapsed_per_frame) > 0) {
            if (this.lockFrame >= 0 && this.lockFrame <= this.frameCount) {
                break;
            }
            elapsedMs = (0, Utils_1.fixNumber)(elapsedMs - this._elapsed_per_frame);
            this.tickFrame();
        }
        this.leftElapsed = elapsedMs;
    };
    EcsWorld.prototype.tickFrame = function () {
        if (!this.enable || this._exit) {
            return;
        }
        for (var i = 0; i < this.systems.length; i++) {
            this.systems[i].updateSystem();
        }
        for (var i = 0; i < this._recover_entities.length; i++) {
            this._recover_entities[i].recover();
        }
        this._recover_entities = [];
        this.frameCount++;
    };
    EcsWorld.prototype.addComponent = function (entity, compClz) {
        var oldNotMatchedGroups = Array.from(this.groupMap.values()).filter(function (i) { return !i.isMatch(entity); });
        var compId = compClz.__compId;
        var comp = new compClz();
        entity.componentMap.set(compId, comp);
        entity.enabledComponentMap.set(compId, comp);
        var newMatchedGroups = oldNotMatchedGroups.filter(function (i) { return i.isMatch(entity); });
        newMatchedGroups.forEach(function (i) { return i.add(entity); });
        var compIds = Array.from(entity.componentMap.keys());
        var compClzs = Array.from(entity.componentMap.values()).map(function (i) { return i.constructor; });
        var groupId = EcsEntity_1.EcsEntity.getGroupID(compIds);
        entity.groupId = groupId;
        entity._factory = this.getEntityPool(groupId, compClzs);
        return comp;
    };
    EcsWorld.prototype.removeComponent = function (entity, compClz) {
        var oldMatchedGroups = Array.from(this.groupMap.values()).filter(function (i) { return i.isMatch(entity); });
        var compId = compClz.__compId;
        entity.componentMap.delete(compId);
        entity.enabledComponentMap.delete(compId);
        var nomoreMatchedGroups = oldMatchedGroups.filter(function (i) { return !i.isMatch(entity); });
        nomoreMatchedGroups.forEach(function (i) { return i.remove(entity); });
        var compIds = Array.from(entity.componentMap.keys());
        var compClzs = Array.from(entity.componentMap.values()).map(function (i) { return i.constructor; });
        var groupId = EcsEntity_1.EcsEntity.getGroupID(compIds);
        entity.groupId = groupId;
        this.getGroupByGroupId.apply(this, __spreadArray([groupId], compIds, false)).add(entity);
        entity._factory = this.getEntityPool(groupId, compClzs);
    };
    // public __onEnableComponent(entity: EcsEntity, id: number): void {
    // 	if (entity.valid) {
    // 		entity.enabledComponentMap.
    // 		let _oldID = entity.__componentsID - Math.pow(2, id);
    // 		let _newID = entity.__componentsID;
    // 		for (let k in this._groups) {
    // 			let _group: EcsEntityGroup = this._groups[k];
    // 			if (_group.isMatch(_newID) && !_group.isMatch(_oldID)) {
    // 				_group.add(this, entity);
    // 			}
    // 		}
    // 	}
    // }
    // public __onDisableComponent(entity: EcsEntity, id: string): void {
    // 	if (entity.valid) {
    // 		let _oldID = entity.__componentsID + Math.pow(2, id);
    // 		let _newID = entity.__componentsID;
    // 		for (let k in this._groups) {
    // 			let _group: EcsEntityGroup = this._groups[k];
    // 			if (_group.isMatch(_oldID) && !_group.isMatch(_newID)) {
    // 				_group.remove(this, entity);
    // 			}
    // 		}
    // 	}
    // }
    // public __onRemoveComponent(entity: EcsEntity,compIds:number[], id: number): void {
    // 	if (entity.valid) {
    // 		let oldCompIds = compIds.concat(id)
    // 		for (let k in this.groupMap) {
    // 			let _group: EcsEntityGroup = this.groupMap[k];
    // 			if (_group.isMatch(entity) && !_group.isMatch(...compIds)) {
    // 				_group.remove(this, entity);
    // 			}
    // 		}
    // 	}
    // }
    // public __onReplaceComponent(entity: EcsEntity, id: number): void {
    // 	if (entity.valid) {
    // 		for (let k in this._groups) {
    // 			let _group: EcsEntityGroup = this._groups[k];
    // 			if (_group.isMatch(entity.__componentsID)) {
    // 				_group.replace(entity, id);
    // 			}
    // 		}
    // 	}
    // }
    EcsWorld.prototype.getComponentOwner = function (comp) {
        return this.getEntity(comp.__ownerId);
    };
    /**获取两帧之间的时间差(毫秒) */
    EcsWorld.prototype.msBetweenFrames = function (startFrame, endFrame) {
        return (endFrame - startFrame) * this._elapsed_per_frame;
    };
    return EcsWorld;
}());
exports.EcsWorld = EcsWorld;
